//+------------------------------------------------------------------+
//|                                                     DateTime.mqh |
//|                             Copyright 2000-2025, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+

//+------------------------------------------------------------------+
//| CDateTime 结构：MQL5 日期时间处理扩展结构                          |
//| 核心定位：继承自 MQL5 原生 MqlDateTime 结构，扩展日期时间的操作能力， |
//|           提供日期时间格式化、字段修改、增减计算、合法性校验等功能，   |
//|           解决原生结构仅支持数据存储、缺乏操作方法的问题             |
//| 核心价值：标准化日期时间处理流程，避免重复开发（如月份名称转换、日期   |
//|           增减计算等），适配 EA 中时间相关场景（如订单到期时间计算、   |
//|           交易时段判断、历史数据筛选等）                             |
//| 适用场景：所有需要复杂日期时间操作的 MQL5 程序（EA、指标、脚本），     |
//|           尤其适合跨时区交易、时间窗口策略、订单有效期管理等场景       |
//+------------------------------------------------------------------+
struct CDateTime : public MqlDateTime
  {
   //----------------------------------------------------------------
   // 日期时间格式化接口：获取月份/星期的名称（完整/缩写）
   //----------------------------------------------------------------
   /**
    * @brief 根据月份编号获取完整月份名称（如 1→"January"）
    * @param num [in] 月份编号（1=1月，12=12月，超出范围返回 "Bad month"）
    * @return string - 完整月份名称（英文），无效编号返回错误提示
    */
   string            MonthName(const int num) const;

   /**
    * @brief 根据月份编号获取月份缩写（如 1→"jan"）
    * @param num [in] 月份编号（1=1月，12=12月，超出范围返回 "Bad month"）
    * @return string - 月份缩写（英文小写），无效编号返回错误提示
    */
   string            ShortMonthName(const int num) const;

   /**
    * @brief 根据星期编号获取完整星期名称（如 0→"Sunday"）
    * @param num [in] 星期编号（0=周日，1=周一，...，6=周六，超出范围返回 "Bad day of week"）
    * @return string - 完整星期名称（英文），无效编号返回错误提示
    */
   string            DayName(const int num) const;

   /**
    * @brief 根据星期编号获取星期缩写（如 0→"Su"）
    * @param num [in] 星期编号（0=周日，1=周一，...，6=周六，超出范围返回 "Bad day of week"）
    * @return string - 星期缩写（英文），无效编号返回错误提示
    */
   string            ShortDayName(const int num) const;

   /**
    * @brief 获取当前结构中月份的完整名称（调用 MonthName(mon)）
    * @return string - 完整月份名称（英文），依赖结构中 mon 字段（1-12）
    */
   string            MonthName(void)               const { return(MonthName(mon));            }

   /**
    * @brief 获取当前结构中月份的缩写（调用 ShortMonthName(mon)）
    * @return string - 月份缩写（英文小写），依赖结构中 mon 字段（1-12）
    */
   string            ShortMonthName(void)          const { return(ShortMonthName(mon));       }

   /**
    * @brief 获取当前结构中星期的完整名称（调用 DayName(day_of_week)）
    * @return string - 完整星期名称（英文），依赖结构中 day_of_week 字段（0-6）
    */
   string            DayName(void)                 const { return(DayName(day_of_week));      }

   /**
    * @brief 获取当前结构中星期的缩写（调用 ShortDayName(day_of_week)）
    * @return string - 星期缩写（英文），依赖结构中 day_of_week 字段（0-6）
    */
   string            ShortDayName(void)            const { return(ShortDayName(day_of_week)); }

   /**
    * @brief 获取当前月份的总天数（自动处理平年/闰年2月天数）
    * @return int - 当月天数（如1月31天、平年2月28天、闰年2月29天），无效月份返回0
    */
   int               DaysInMonth(void) const;

   //----------------------------------------------------------------
   // 日期时间数据转换接口：在结构与 datetime 类型间转换
   //----------------------------------------------------------------
   /**
    * @brief 将当前结构数据转换为 MQL5 原生 datetime 类型（时间戳）
    * @return datetime - 对应的时间戳（从1970-01-01 00:00:00 UTC 开始的秒数）
    * @note 依赖结构中 year、mon、day、hour、min、sec 字段的合法性
    */
   datetime          DateTime(void)                      { return(StructToTime(*this));       }

   /**
    * @brief 将 datetime 时间戳转换为结构数据（覆盖当前结构字段）
    * @param value [in] 待转换的 datetime 时间戳
    * @note 调用 MQL5 原生函数 TimeToStruct()，自动填充 year、mon、day、hour、min、sec 等所有字段
    */
   void              DateTime(const datetime value)      { TimeToStruct(value, *this);        }

   /**
    * @brief 将另一个 MqlDateTime 结构的数据复制到当前结构
    * @param value [in] 源 MqlDateTime 结构（包含日期时间字段）
    * @note 直接内存拷贝，覆盖当前结构的所有字段（包括 year、mon、day、hour、min、sec 等）
    */
   void              DateTime(const MqlDateTime& value)  { (MqlDateTime&)*this = value;       }

   /**
    * @brief 仅设置当前结构的“日期”部分（年/月/日），保留“时间”部分（时/分/秒）
    * @param value [in] 包含目标日期的 datetime 时间戳
    * @note 1. 提取 value 中的年、月、日信息，覆盖当前结构的 year、mon、day 字段；
    *       2. 调用 DayCheck() 确保日期合法性（如避免 2月30日 这类无效日期）。
    */
   void              Date(const datetime value);

   /**
    * @brief 仅设置当前结构的“日期”部分（年/月/日），保留“时间”部分（时/分/秒）
    * @param value [in] 源 MqlDateTime 结构（提供年/月/日信息）
    * @note 1. 复制 value 中的 year、mon、day 字段到当前结构；
    *       2. 调用 DayCheck() 确保日期合法性。
    */
   void              Date(const MqlDateTime &value);

   /**
    * @brief 仅设置当前结构的“时间”部分（时/分/秒），保留“日期”部分（年/月/日）
    * @param value [in] 包含目标时间的 datetime 时间戳
    * @note 提取 value 中的时、分、秒信息，覆盖当前结构的 hour、min、sec 字段
    */
   void              Time(const datetime value);

   /**
    * @brief 仅设置当前结构的“时间”部分（时/分/秒），保留“日期”部分（年/月/日）
    * @param value [in] 源 MqlDateTime 结构（提供时/分/秒信息）
    * @note 复制 value 中的 hour、min、sec 字段到当前结构
    */
   void              Time(const MqlDateTime &value);

   //----------------------------------------------------------------
   // 日期时间字段设置接口：单独修改时/分/秒/日/月/年（带合法性校验）
   //----------------------------------------------------------------
   /**
    * @brief 设置“秒”字段（0-59）
    * @param value [in] 目标秒数（仅 0≤value<60 有效，超出范围不修改）
    */
   void              Sec(const int value);

   /**
    * @brief 设置“分”字段（0-59）
    * @param value [in] 目标分钟数（仅 0≤value<60 有效，超出范围不修改）
    */
   void              Min(const int value);

   /**
    * @brief 设置“时”字段（0-23）
    * @param value [in] 目标小时数（仅 0≤value<24 有效，超出范围不修改）
    */
   void              Hour(const int value);

   /**
    * @brief 设置“日”字段（1-当月天数）
    * @param value [in] 目标日数（仅 1≤value≤当月天数 有效，超出范围不修改）
    * @note 调用 DayCheck() 确保日期合法性（如当月只有30天，设置31天将被忽略）
    */
   void              Day(const int value);

   /**
    * @brief 设置“月”字段（1-12）
    * @param value [in] 目标月份（仅 1≤value≤12 有效，超出范围不修改）
    * @note 调用 DayCheck() 调整日数（如原日期为3月31日，改为4月后自动调整为30日）
    */
   void              Mon(const int value);

   /**
    * @brief 设置“年”字段（≥1970）
    * @param value [in] 目标年份（仅 value≥1970 有效，低于1970不修改，因 datetime 从1970年开始）
    * @note 调用 DayCheck() 调整日数（如原日期为2月29日，改为平年后自动调整为28日）
    */
   void              Year(const int value);

   //----------------------------------------------------------------
   // 日期时间增减接口：按秒/分/时/日/月/年增减（自动处理进位/借位）
   //----------------------------------------------------------------
   /**
    * @brief 减少指定秒数（支持负数，自动转为增加）
    * @param delta [in] 减少的秒数（正数=减少，负数=增加对应秒数，0=无变化）
    * @note 自动处理进位（如减少1秒到0秒时，分钟减1，秒数变为59）
    */
   void              SecDec(int delta=1);

   /**
    * @brief 增加指定秒数（支持负数，自动转为减少）
    * @param delta [in] 增加的秒数（正数=增加，负数=减少对应秒数，0=无变化）
    * @note 自动处理进位（如增加1秒到60秒时，分钟加1，秒数变为0）
    */
   void              SecInc(int delta=1);

   /**
    * @brief 减少指定分钟数（支持负数，自动转为增加）
    * @param delta [in] 减少的分钟数（正数=减少，负数=增加对应分钟数，0=无变化）
    * @note 自动处理进位（如减少1分钟到0分时，小时减1，分钟变为59）
    */
   void              MinDec(int delta=1);

   /**
    * @brief 增加指定分钟数（支持负数，自动转为减少）
    * @param delta [in] 增加的分钟数（正数=增加，负数=减少对应分钟数，0=无变化）
    * @note 自动处理进位（如增加1分钟到60分时，小时加1，分钟变为0）
    */
   void              MinInc(int delta=1);

   /**
    * @brief 减少指定小时数（支持负数，自动转为增加）
    * @param delta [in] 减少的小时数（正数=减少，负数=增加对应小时数，0=无变化）
    * @note 自动处理进位（如减少1小时到0时时，天数减1，小时变为23）
    */
   void              HourDec(int delta=1);

   /**
    * @brief 增加指定小时数（支持负数，自动转为减少）
    * @param delta [in] 增加的小时数（正数=增加，负数=减少对应小时数，0=无变化）
    * @note 自动处理进位（如增加1小时到24时时，天数加1，小时变为0）
    */
   void              HourInc(int delta=1);

   /**
    * @brief 减少指定天数（支持负数，自动转为增加）
    * @param delta [in] 减少的天数（正数=减少，负数=增加对应天数，0=无变化）
    * @note 自动处理跨月/跨年（如从3月1日减少1天，自动转为2月最后一天）
    */
   void              DayDec(int delta=1);

   /**
    * @brief 增加指定天数（支持负数，自动转为减少）
    * @param delta [in] 增加的天数（正数=增加，负数=减少对应天数，0=无变化）
    * @note 自动处理跨月/跨年（如从2月28日增加1天，闰年转为2月29日，平年转为3月1日）
    */
   void              DayInc(int delta=1);

   /**
    * @brief 减少指定月数（支持负数，自动转为增加）
    * @param delta [in] 减少的月数（正数=减少，负数=增加对应月数，0=无变化）
    * @note 自动处理跨年（如从1月减少1个月，转为上一年12月），并调用 DayCheck() 调整日数
    */
   void              MonDec(int delta=1);

   /**
    * @brief 增加指定月数（支持负数，自动转为减少）
    * @param delta [in] 增加的月数（正数=增加，负数=减少对应月数，0=无变化）
    * @note 自动处理跨年（如从12月增加1个月，转为下一年1月），并调用 DayCheck() 调整日数
    */
   void              MonInc(int delta=1);

   /**
    * @brief 减少指定年数（支持负数，自动转为增加）
    * @param delta [in] 减少的年数（正数=减少，负数=增加对应年数，0=无变化）
    * @note 调用 DayCheck() 调整日数（如从闰年2月29日减少1年，转为平年2月28日）
    */
   void              YearDec(int delta=1);

   /**
    * @brief 增加指定年数（支持负数，自动转为减少）
    * @param delta [in] 增加的年数（正数=增加，负数=减少对应年数，0=无变化）
    * @note 调用 DayCheck() 调整日数（如从平年2月28日增加1年，若为闰年转为2月29日）
    */
   void              YearInc(int delta=1);

   //----------------------------------------------------------------
   // 日期合法性校验接口：确保日数与当月天数匹配
   //----------------------------------------------------------------
   /**
    * @brief 校验并修正“日”字段的合法性（核心辅助方法）
    * @note 1. 若当前 day 大于当月总天数（DaysInMonth()），自动将 day 设为当月最后一天；
    *       2. 调用 TimeToStruct() 刷新 day_of_week（星期）和 day_of_year（年内天数）字段；
    *       3. 在修改年/月/日后自动调用，确保结构数据始终合法。
    */
   void              DayCheck(void);
  };
//+------------------------------------------------------------------+